// Copyright (C) Kumo inc. and its affiliates.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//
#pragma once

#include <pollux/type/subfield.h>

namespace kumo::pollux::common {
    class Tokenizer {
    public:
        enum class State {
            // We have computed the next element and haven't returned it yet.
            kReady,

            // We haven't yet computed or have already returned the element.
            kNotReady,

            // We have reached the end of the data and are finished.
            kDone,

            // We've suffered an exception and are kaput.
            kFailed,
        };

        // Separators: the customized separators to tokenize field name.
        explicit Tokenizer(
            const std::string &path,
            const std::shared_ptr<Separators> &separators);

        bool hasNext();

        std::unique_ptr<Subfield::PathElement> next();

    private:
        const std::string path_;
        // Customized separators to tokenize field name.
        std::shared_ptr<Separators> separators_;

        int index_;
        State state;
        bool firstSegment = true;
        std::unique_ptr<Subfield::PathElement> next_;

        bool hasNextCharacter();

        std::unique_ptr<Subfield::PathElement> computeNext();

        // Returns whether the expected char is a separator and
        // can be found.
        bool tryMatchSeparator(char expected);

        void match(char expected);

        bool tryMatch(char expected);

        std::unique_ptr<Subfield::PathElement> matchPathSegment();

        std::unique_ptr<Subfield::PathElement> matchUnquotedSubscript();

        std::unique_ptr<Subfield::PathElement> matchQuotedSubscript();

        std::string toString();

        bool tryToComputeNext();

        void invalidSubfieldPath();

        bool isUnquotedPathCharacter(char c);

        bool isUnquotedSubscriptCharacter(char c);

        void nextCharacter();

        char peekCharacter();

        std::unique_ptr<Subfield::PathElement> matchWildcardSubscript();
    };
} // namespace kumo::pollux::common
